﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ys_dec
{
    class LzssYs
    {
        uint count;
        uint flag;
        public LzssYs()
        {
            this.count = 0;
            this.flag = 0;
        }

        private uint GetBit(BinaryReader br)
        {
            if (count == 0)
            {
                flag = br.ReadUInt16();
                //Console.WriteLine(string.Format("flag {0:X4}", flag));
                count = 16;
            }
            uint bit = flag & 1;
            flag >>= 1; count--;
            return bit;
        }
        public void YsLzss_dec(BinaryReader br, BinaryWriter bw)
        {
            uint num1 = br.ReadUInt32();
            uint dec_size = br.ReadUInt32();
            int out_size = 0;
            //分段压缩数据
            while (out_size < dec_size)
            {
                out_size += lzss_dec(br, bw);
                br.ReadByte();
                //Console.WriteLine(bw.BaseStream.Position);
            }
        }
        private int lzss_dec(BinaryReader br, BinaryWriter bw)
        {
            byte[] buf = new byte[0x10000];
            int pos = 0;
            uint size = br.ReadUInt16();
            br.ReadByte();
            uint read_cnt = 2;

            flag = br.ReadByte();
            count = 8;
            long start = br.BaseStream.Position;
            try
            {
                while (br.BaseStream.Position < start + size)
                {
                    uint back, forward;
                    uint bit = GetBit(br);
                    if (bit == 0)//直接输出
                    {
                        byte b = br.ReadByte();
                        read_cnt++;
                        buf[pos++] = b;
                        //Console.Write(string.Format("{0:X2} ", b));
                    }
                    else
                    {
                        //计算back值-------------------------------------------------------
                        bit = GetBit(br);

                        if (bit == 0)
                        {
                            back = br.ReadByte();
                        }
                        else
                        {
                            uint flagbits = 0;
                            for (int i = 0; i < 5; i++)
                            {
                                bit = GetBit(br);
                                flagbits <<= 1;
                                flagbits += bit;
                            }
                            back = (flagbits << 8) + br.ReadByte();

                            if (back == 0)
                            {
                                bw.Write(buf, 0, pos);
                                return pos;
                            }
                            if (back == 1)
                            {
                                uint rep_typ = GetBit(br);
                                uint rep;

                                flagbits = 0;
                                for (int i = 0; i < 4; i++)
                                {
                                    bit = GetBit(br);
                                    flagbits <<= 1;
                                    flagbits += bit;
                                }

                                if (rep_typ == 1)
                                {
                                    rep = (flagbits << 8) + 0xE;
                                    rep += br.ReadByte();
                                }
                                else
                                {
                                    rep = flagbits + 0xEu;
                                }
                                byte b = br.ReadByte();
                                for (int i = 0; i < rep; i++)
                                {
                                    buf[pos++] = b;
                                }
                                //Console.WriteLine(string.Format("{0:X2} x {1}", b, rep));
                                continue;
                            }
                        }
                        //Console.WriteLine("back =\t" + back.ToString());
                        int pos2 = pos - (int)back;
                        if (pos2 < 0) 
                            throw new Exception("back too much"); ;
                        //计算forward值------------------------------------------------------------------------
                        forward = 1;
                        do
                        {
                            bit = GetBit(br);
                            forward++;
                        } while (bit == 0 && forward < 5);

                        if (bit == 0)
                        {
                            bit = GetBit(br);

                            if (bit == 1)
                            {
                                uint flagbit = 0;
                                for (int i = 0; i < 3; i++)
                                {
                                    bit = GetBit(br);
                                    flagbit <<= 1; flagbit += bit;
                                }
                                forward = flagbit + 6;
                            }
                            else
                            {
                                forward = br.ReadByte() + (uint)0xE;
                                read_cnt++;
                            }
                        }
                        //Console.WriteLine("forward =\t" + string.Format("{0}", forward));
                        for (int i = 0; i < forward; i++)
                        {
                            buf[pos++] = buf[pos2++];
                            //Console.Write(string.Format("{0:X2} ", buf[pos2 - 1]));
                            //Console.Write(' ');
                        }

                    }
                }
            }
            catch (Exception e)
            {
                bw.Write(buf, 0, pos - 1);
                bw.Close();
                Console.WriteLine(e.Message);
                Environment.Exit(-1);
            }
            bw.Write(buf, 0, pos);
            //Console.WriteLine(bw.BaseStream.Position);
            return pos;
        }
        public int YsLzss_enc(BinaryReader br, BinaryWriter bw)
        {
            bw.Write(0x8);
            bw.Write((int)(br.BaseStream.Length));
            do
            {
                byte[] buf = br.ReadBytes(0x10000);
                Putbb pb = new Putbb();
                lzss_enc(buf, pb);
                byte[] cbuf = pb.GetCompressData();
                bw.Write((UInt16)cbuf.Length);
                bw.Write(cbuf);
                bw.Write((byte)2);
            } while (br.BaseStream.Position < br.BaseStream.Length);
            return (int)bw.BaseStream.Length;
        }
        private void lzss_enc(Byte[] buf, Putbb pb)
        {
            int maxf, maxb;
            int pos = 0;

            while (pos < buf.Length)
            {
                int tp = pos + 1;
                int rpos, bpos;
                maxf = 0;
                maxb = 0;
                while (tp < buf.Length && buf[pos] == buf[tp] && tp - pos < 0x100E)
                    tp++;
                rpos = tp;
                for (int back = 1; back < 0x2000 && back <= pos; back++)
                {
                    tp = pos;
                    int forward = 0;
                    while (tp < buf.Length && buf[tp - back] == buf[tp] && forward < 0x10D)
                    {
                        forward++;
                        tp++;
                    }
                    if (forward > maxf)
                    {
                        maxf = forward;
                        maxb = back;
                    }
                    if (maxf > 0x10D) break;
                }
                bpos = pos + maxf;


                if (bpos < rpos && rpos - pos >= 0xE)
                {
                    tp = rpos;
                    int rep = tp - pos - 0xE;
                    Console.WriteLine(String.Format("Byte 0x{0:X2} rep {1}", buf[pos], rep));
                    pb.PutBits(0x3, 2);
                    pb.PutBits(0, 5);
                    pb.PutByte(1);
                    if (rep < 0x10)
                    {
                        pb.PutBit(0);
                        pb.PutBits(rep, 4);
                    }
                    else
                    {
                        byte b = (byte)(rep & 0xFF);
                        rep >>= 8;
                        byte hb = (byte)(rep);
                        pb.PutBit(1);
                        pb.PutBits(hb, 4);
                        pb.PutByte(b);
                    }
                    pb.PutByte(buf[pos]);
                    pos = tp;
                }
                else
                {
                    if (maxf < 2)
                    {
                        Console.WriteLine(String.Format("Same Byte 0x{0:X2}", buf[pos]));
                        pb.PutBit(0);
                        pb.PutByte(buf[pos]);
                        pos++;
                    }
                    else
                    {
                        //back
                        Console.WriteLine(String.Format("B {0}, F {1}", maxb, maxf));
                        pb.PutBit(1);
                        if (maxb <= 0xFF)
                        {
                            pb.PutBit(0);
                            pb.PutByte((byte)maxb);
                        }
                        else
                        {
                            pb.PutBit(1);
                            byte b = (byte)(maxb & 0xFF);
                            byte hb = (byte)(maxb >> 8);
                            pb.PutBits(hb, 5);
                            pb.PutByte(b);
                        }

                        //forward
                        if (maxf <= 5)
                        {
                            pb.PutBits(0, maxf - 2);
                            pb.PutBit(1);
                        }
                        else
                        {
                            int maxf2 = maxf;
                            pb.PutBits(0, 4);
                            maxf2 -= 6;
                            if (maxf2 < 8)
                            {
                                pb.PutBit(1);
                                pb.PutBits(maxf2 & 0x7, 3);
                            }
                            else
                            {
                                pb.PutBit(0);
                                maxf2 -= 8;
                                pb.PutByte((byte)maxf2);
                                if (maxf2 > 0xFF) throw new Exception("Too much forward");
                            }
                        }
                        pos += maxf;
                    }
                }
            }
            pb.PutBits(0x3, 2);
            pb.PutBits(0, 5);
            pb.PutByte(0);
        }
    }
}
